package com.linkmcn.gateway.filter;

import com.linkmcn.cloud.props.LinkmcnUaaProperties;
import com.linkmcn.core.constans.LinkmcnConstants;
import com.linkmcn.core.constans.TenantConstant;
import com.linkmcn.core.exception.TokenException;
import com.linkmcn.core.util.ResponseUtil;
import com.linkmcn.gateway.util.TokenUtil;
import io.jsonwebtoken.Claims;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.function.Consumer;

/**
 * 网关统一的token验证
 */
@Slf4j
@Component
@AllArgsConstructor
public class UaaFilter implements GlobalFilter, Ordered {

    private final LinkmcnUaaProperties linkmcnUaaProperties;
    public static final String PRE_FIX = "link/";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 如果未启用网关验证，则跳过
        if (!linkmcnUaaProperties.getEnable()) {
            return chain.filter(exchange);
        }
        log.info("getIgnoreUrl:{}", linkmcnUaaProperties.getIgnoreUrl());

        //　如果在忽略的url里，则跳过
        String path = replacePrefix(exchange.getRequest().getURI().getPath());
        if (ignore(path)) {
            return chain.filter(exchange);
        }

        ServerHttpResponse resp = exchange.getResponse();

        String token = null;
        try {
            //获取请求中携带的token
            token = TokenUtil.getToken(exchange.getRequest());
            Assert.notNull(token, "请求未携带token");
            //验证token
            Claims claims = TokenUtil.getClaims(token);
            Integer userId = (Integer) claims.get("userId");
            Integer tenantId = (Integer) claims.get("tenantId");

            Consumer<HttpHeaders> httpHeaders = httpHeader -> {
                httpHeader.set(LinkmcnConstants.USER_ID, String.valueOf(userId));
                httpHeader.set(TenantConstant.TENANT_HEADER, String.valueOf(tenantId));
            };
            ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeaders).build();
            ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build();
            return chain.filter(build);
        } catch (TokenException e) {
            return unauthorized(resp, e.getMessage());
        }
    }

    /**
     * 检查是否忽略url
     *
     * @param path 路径
     * @return boolean
     */
    private boolean ignore(String path) {
        return linkmcnUaaProperties.getIgnoreUrl().stream()
                .map(url -> url.replace("/**", ""))
                .anyMatch(path::startsWith);
    }

    /**
     * 移除模块前缀
     *
     * @param path 路径
     * @return String
     */
    private String replacePrefix(String path) {
        if (path.startsWith(PRE_FIX)) {
            return path.substring(path.indexOf("/", 1));
        }
        return path;
    }

    private Mono<Void> unauthorized(ServerHttpResponse resp, String msg) {
        return ResponseUtil.webFluxResponseWriter(resp, "application/json;charset=UTF-8", HttpStatus.UNAUTHORIZED, msg);
    }

    @Override
    public int getOrder() {
        return -200;
    }

}
